<template>
  <div class="nav-bar">
    <div class="nav-btn zoom-in" title="放大" :class="{'active' : zoomRadioIn }" @click="onZoomIn">
      <div class="icon" />
    </div>
    <div ref="zoomPan" class="zoom-pan">
      <div
        class="origin"
        :style="{ 'transform': 'translate(0, ' + this.zoomPan.origin + 'px)' }"
        @click="onRestoreSize"
      />
      <div
        class="indicator"
        :style="{
          'transform': 'translate(0px, ' + this.zoomPan.indicator + 'px)',
          'transition': 'transform 200ms'
        }"
      />
    </div>
    <div class="nav-btn zoom-out" title="缩小" :class="{'active' : zoomRadioOut }" @click="onZoomOut">
      <div class="icon" />
    </div>
    <div class="nav-btn hand" title="允许拖拽" :class="{'active' : enableHand }" @click="onHand">
      <div class="icon" />
    </div>
    <div class="nav-btn camera" title="定位根节点" @click="onLocateToOrigin">
      <div class="icon" />
    </div>
    <div
      class="nav-btn nav-trigger"
      title="导航器"
      :class="{'active' : showPreviewer }"
      @click="() => this.showPreviewer = !this.showPreviewer"
    >
      <div class="icon" />
    </div>
    <div v-show="showPreviewer" ref="navPreviewer" class="nav-previewer" />
  </div>
</template>

<script>
export default {
  name: 'Navbar',
  props: {
    minder: {
      type: Object
    }
  },
  data() {
    return {
      // 缩放比例
      zoom: [10, 20, 30, 50, 80, 100, 120, 150, 200],
      // 中心点
      origin: 100,
      // 当前缩放
      indicator: 100,
      // 放大到最大
      zoomRadioIn: false,
      // 缩小到最小
      zoomRadioOut: false,
      // 启用拖拽
      enableHand: false,
      // 显示缩略图
      showPreviewer: false,
      // 缩放位置
      zoomPan: {
        origin: 0,
        indicator: 0
      },
      // 缩略图
      previewer: {
        dragging: false,
        paper: null,
        visibleRect: null,
        visibleView: null,
        contentView: null,
        nodeThumb: null,
        connectionThumb: null
      }
    }
  },
  watch: {
    // minder 渲染完成后触发navbar初始化 只会触发一次
    minder: function() {
      this.init()
    },
    // 监听导航器显示隐藏
    showPreviewer: function() {
      this.toggleBindPreviewer()
    }
  },
  mounted() {
    // 初始化滑轨位置
    this.zoomPan.origin = this.calePanHeight(this.origin)
    this.zoomPan.indicator = this.calePanHeight(this.indicator)
  },
  destroyed() {
    this.previewer.paper.off('mouseup')
    this.previewer.paper.off('mousemove')
    this.previewer.paper.off('mousedown')
    this.previewer.paper.off()

    this.minder.off('zoom', this.onZoom)
    this.minder.off('layout layoutallfinish', this.updateContentView)
    this.minder.off('viewchange', this.updateVisibleView)
  },
  methods: {
    // 初始化
    init() {
      // 配置脑图缩放比例
      this.minder.setDefaultOptions({ zoom: this.zoom })
      // 渲染缩略图
      this.renderPaper()
      // 注册事件
      this.registerEvent()
    },
    // 放大
    onZoomIn() {
      this.minder.execCommand('zoomIn')
    },
    // 恢复
    onRestoreSize() {
      this.minder.execCommand('zoom', this.origin)
    },
    // 缩小
    onZoomOut() {
      this.minder.execCommand('zoomOut')
    },
    // 允许拖拽
    onHand() {
      this.enableHand = !this.enableHand
      this.minder.execCommand('hand')
    },
    // 定位根结点
    onLocateToOrigin() {
      this.minder.execCommand('camera', this.minder.getRoot(), 600)
    },
    /**
             * 计算圆点在滑轨的位置
             * @param current 当前缩放参数
             * @returns {number} 高度
             */
    calePanHeight(current) {
      const max = this.zoom[this.zoom.length - 1]
      const min = this.zoom[0]
      const range = max - min
      const rate = 1 - (current - min) / range
      return Math.round(this.$refs.zoomPan.offsetHeight * rate)
    },
    // 注册事件
    registerEvent() {
      // 脑图Zoom事件触发
      this.minder.on('zoom', this.onZoom)
    },
    onZoom(e) {
      this.zoomPan.indicator = this.calePanHeight(e.zoom)
      this.zoomRadioIn = e.zoom === this.zoom[this.zoom.length - 1]
      this.zoomRadioOut = e.zoom === this.zoom[0]
    },
    // 渲染缩略图
    renderPaper() {
      // 画布，渲染缩略图
      this.previewer.paper = new kity.Paper(this.$refs.navPreviewer)

      // 用两个路径来挥之节点和连线的缩略图
      this.previewer.nodeThumb = this.previewer.paper.put(new kity.Path())
      this.previewer.connectionThumb = this.previewer.paper.put(new kity.Path())

      // 表示可视区域的矩形
      this.previewer.visibleRect = this.previewer.paper.put(new kity.Rect(100, 100).stroke('#48b3ff', '1%'))

      // 画布 - 鼠标按下事件
      this.previewer.paper.on('mousedown', (e) => {
        this.previewer.dragging = true
        this.moveView(e.getPosition('top'), 200)
        // $previewNavigator.addClass("grab");
      })

      // 画布 - 鼠标移动事件
      this.previewer.paper.on('mousemove', (e) => {
        if (this.previewer.dragging) {
          this.moveView(e.getPosition('top'))
        }
      })

      this.previewer.paper.on('mouseup', (e) => {
        this.previewer.dragging = false
      })

      this.toggleBindPreviewer()
    },
    // toggle缩略图绑定事件
    toggleBindPreviewer() {
      if (this.showPreviewer) {
        this.minder.on('layout layoutallfinish', this.updateContentView)
        this.minder.on('viewchange', this.updateVisibleView)
        this.updateContentView()
        this.updateVisibleView()
      } else {
        this.minder.off('layout layoutallfinish', this.updateContentView)
        this.minder.off('viewchange', this.updateVisibleView)
      }
    },
    // 拖动可视区域
    moveView(center, duration) {
      let box = this.previewer.visibleView
      center.x = -center.x
      center.y = -center.y
      const viewMatrix = this.minder.getPaper().getViewPortMatrix()
      box = viewMatrix.transformBox(box)
      const targetPosition = center.offset(box.width / 2, box.height / 2)
      this.minder.getViewDragger().moveTo(targetPosition, duration)
    },
    // 更新缩略图
    updateContentView() {
      this.previewer.contentView = this.minder.getRenderContainer().getBoundaryBox()
      const padding = 30
      this.previewer.paper.setViewBox(this.previewer.contentView.x - padding - 0.5, this.previewer.contentView.y - padding - 0.5,
        this.previewer.contentView.width + padding * 2 + 1, this.previewer.contentView.height + padding * 2 + 1)
      const nodePathData = []; const connectionThumbData = []
      this.minder.getRoot().traverse(function(node) {
        const box = node.getLayoutBox()

        function pathHandler(nodePathData, x, y, width, height) {
          nodePathData.push('M', x, y, 'h', width, 'v', height, 'h', -width, 'z')
        }

        pathHandler(nodePathData, box.x, box.y, box.width, box.height)
        if (node.getConnection() && node.parent && node.parent.isExpanded()) {
          connectionThumbData.push(node.getConnection().getPathData())
        }
      })

      this.previewer.paper.setStyle('background', '#FFFFFF')
      if (nodePathData.length) {
        this.previewer.nodeThumb.fill(this.minder.getStyle('root-background')).setPathData(nodePathData)
      } else {
        this.previewer.nodeThumb.setPathData(null)
      }
      if (connectionThumbData.length) {
        this.previewer.connectionThumb.stroke(this.minder.getStyle('connect-color'), '0.5%').setPathData(connectionThumbData)
      } else {
        this.previewer.connectionThumb.setPathData(null)
      }
    },
    // 更新可视区域
    updateVisibleView() {
      this.previewer.visibleView = this.minder.getViewDragger().getView()
      this.previewer.visibleRect.setBox(this.previewer.visibleView.intersect(this.previewer.contentView))
    }
  }
}
</script>

<style>
.nav-bar {
    position: absolute;
    width: 35px;
    height: 220px;
    padding: 5px 0;
    bottom: 10px;
    color: #fff;
    border-radius: 4px;
    z-index: 10;
    background: #48b3ff;
    box-shadow: 0px 2px 10px 0px rgba(72, 179, 255, 0.3);
    transition: -webkit-transform 0.7s 0.1s ease;
    transition: transform .7s 0.1s ease;
}

.nav-bar .nav-btn {
    width: 35px;
    height: 24px;
    line-height: 24px;
    text-align: center;
}

.nav-bar .nav-btn .icon {
    background: url(./lib/icons.png);
    width: 23px;
    height: 23px;
    margin: 2px auto;
    display: block;
}

.nav-bar .nav-btn.active {
    background-color: #A3D9FF;
}

.nav-bar .zoom-in .icon {
    background-position: -1px -731px;
}

.nav-bar .zoom-out .icon {
    background-position: -2px -754px;
}

.nav-bar .hand .icon {
    background-position: -3px -778px;
    width: 22px;
    height: 21px;
    margin: 0 auto;
}

.nav-bar .camera .icon {
    background-position: 0px -870px;
    width: 25px;
    height: 25px;
    margin: 0 auto;
}

.nav-bar .nav-trigger .icon {
    background-position: -1px -845px;
    width: 25px;
    height: 25px;
    margin: 0 auto;
}

.nav-bar .zoom-pan {
    width: 2px;
    height: 70px;
    box-shadow: 0 1px #48b3ff;
    position: relative;
    background: white;
    margin: 3px auto;
    overflow: visible;
}

.nav-bar .zoom-pan .origin {
    position: absolute;
    width: 20px;
    height: 8px;
    left: -9px;
    margin-top: -4px;
    background: transparent;
}

.nav-bar .zoom-pan .origin:after {
    content: ' ';
    display: block;
    width: 6px;
    height: 2px;
    background: white;
    left: 7px;
    top: 3px;
    position: absolute;
}

.nav-bar .zoom-pan .indicator {
    position: absolute;
    width: 8px;
    height: 8px;
    left: -3px;
    background: white;
    border-radius: 100%;
    margin-top: -4px;
}

.nav-previewer {
    background: #fff;
    width: 140px;
    height: 120px;
    position: absolute;
    left: 45px;
    bottom: 30px;
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
    border-radius: 0 2px 2px 0;
    padding: 1px;
    z-index: 9;
    cursor: crosshair;
    transition: -webkit-transform 0.7s 0.1s ease;
    transition: transform .7s 0.1s ease;
}

.nav-previewer.grab {
    cursor: move;
    cursor: -webkit-grabbing;
    cursor: -moz-grabbing;
    cursor: grabbing;
}
</style>
